#include <rtthread.h>
#include <rtdevice.h>
#include "./drv_pcf8563.h"

struct pcf8563_config
{
    rt_uint32_t size;
    rt_uint16_t addr;
    rt_uint16_t flags;
};

struct pcf8563_device
{
    struct rt_device parent;
    struct rt_i2c_bus_device *bus;
};

static struct pcf8563_device pcf8563_drv;

static struct pcf8563_config pcf8563_config = {
    .size = 16,
    .addr = (0xA2 >> 1),
    .flags = 0,
};

static rt_size_t pcf8563_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size);
static rt_size_t pcf8563_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size);

static rt_err_t pcf8563_register(const char *fm_device_name, const char *i2c_bus, void *user_data);

rt_size_t pcf8563_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
{
    struct pcf8563_device *pcf8563;
    const struct pcf8563_config *cfg;
    struct rt_i2c_msg msg[2];
    rt_size_t ret = 0;
    RT_ASSERT(dev != 0);

    pcf8563 = (struct pcf8563_device *)dev;

    RT_ASSERT(pcf8563->parent.user_data != 0);
    cfg = (const struct pcf8563_config *)pcf8563->parent.user_data;

    if (pos > cfg->size)
    {
        return 0;
    }

    if (pos + size > cfg->size)
    {
        size = cfg->size - pos;
    }

    msg[0].addr = cfg->addr;
    msg[0].flags = cfg->flags | RT_I2C_WR;
    msg[0].buf = (rt_uint8_t *)&pos;
    msg[0].len = 1;

    msg[1].addr = cfg->addr;
    msg[1].flags = cfg->flags | RT_I2C_RD;
    msg[1].buf = (rt_uint8_t *)buffer;
    msg[1].len = size;

    ret = rt_i2c_transfer(pcf8563->bus, msg, 2);
    return (ret == 2) ? size : 0;
}

rt_size_t pcf8563_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
{
    struct pcf8563_device *pcf8563;
    const struct pcf8563_config *cfg;
    struct rt_i2c_msg msg[2];
    rt_size_t ret = 0;
    RT_ASSERT(dev != 0);

    pcf8563 = (struct pcf8563_device *)dev;

    RT_ASSERT(pcf8563->parent.user_data != 0);
    cfg = (const struct pcf8563_config *)pcf8563->parent.user_data;

    if (pos > cfg->size)
    {
        return 0;
    }

    if (pos + size > cfg->size)
    {
        size = cfg->size - pos;
    }

    msg[0].addr = cfg->addr;
    msg[0].flags = cfg->flags | RT_I2C_WR;
    msg[0].buf = (rt_uint8_t *)&pos;
    msg[0].len = 1;

    msg[1].addr = cfg->addr;
    msg[1].flags = cfg->flags | RT_I2C_WR | RT_I2C_NO_START;
    msg[1].buf = (rt_uint8_t *)buffer;
    msg[1].len = size;

    ret = rt_i2c_transfer(pcf8563->bus, msg, 2);
    return (ret == 2) ? size : 0;
}

#ifdef RT_USING_DEVICE_OPS
const static struct rt_device pcf8563_ops =
    {
        NULL,
        NULL,
        NULL,
        pcf8563_read,
        pcf8563_write,
        NULL};
#endif

rt_err_t pcf8563_register(const char *fm_device_name, const char *i2c_bus, void *user_data)
{
    struct rt_i2c_bus_device *bus;

    bus = rt_i2c_bus_device_find(i2c_bus);
    if (bus == RT_NULL)
    {
        return RT_ENOSYS;
    }

    pcf8563_drv.bus = bus;
    pcf8563_drv.parent.type = RT_Device_Class_Miscellaneous;
#ifdef RT_USING_DEVICE_OPS
    pcf8563_drv.parent.ops = &pcf8563_ops;
#else
    pcf8563_drv.parent.init = NULL;
    pcf8563_drv.parent.open = NULL;
    pcf8563_drv.parent.close = NULL;
    pcf8563_drv.parent.read = pcf8563_read;
    pcf8563_drv.parent.write = pcf8563_write;
    pcf8563_drv.parent.control = NULL;
#endif

    pcf8563_drv.parent.user_data = user_data;

    return rt_device_register(&pcf8563_drv.parent, fm_device_name, RT_DEVICE_FLAG_RDWR);
}

static int stm32_pcf8563_attach(void)
{
    int result;
    result = pcf8563_register("pcf8563", "i2c1_bus", &pcf8563_config);
    if (RT_ENOSYS == result)
    {
        rt_kprintf("please check pcf8563 device\n");
    }
    return result;
}
INIT_DEVICE_EXPORT(stm32_pcf8563_attach);

void PCF8563_Read_Time(struct pcf8563_time *value)
{
    pcf8563_read(&pcf8563_drv.parent, 0x02, (void *)value, sizeof(struct pcf8563_time) / sizeof(unsigned char)); /* 读取寄存器的值 */
    /* 从BCD转换 */
    char temp = value->second;
    value->second = ((temp & 0x70) >> 4) * 10 + (temp & 0x0F); /* 计算秒 */

    temp = value->minute;
    value->minute = ((temp & 0x70) >> 4) * 10 + (temp & 0x0F); /* 计算分 */

    temp = value->hour;
    value->hour = ((temp & 0x30) >> 4) * 10 + (temp & 0x0F); /* 计算时 */

    temp = value->day;
    value->day = ((temp & 0x30) >> 4) * 10 + (temp & 0x0F); /* 计算天 */

    temp = value->weekday;
    value->weekday = (temp & 0x07); /* 计算星期 */

    temp = value->month;
    value->month = ((temp & 0x10) >> 4) * 10 + (temp & 0x0F); /* 计算月 */

    char year_flag = temp & 0x80; /* 保存世纪标志 */

    temp = value->year;
    value->year = ((temp & 0xF0) >> 4) * 10 + (temp & 0x0F); /* 计算年 */
    if (year_flag)
    {
        value->year += 100;
    }
}

void PCF8563_Set_Time(struct pcf8563_time *value)
{
    /* 转换成BCD */
    char temp = value->second;
    value->second = ((temp / 10) << 4) | (temp % 10); /* 计算秒 */

    temp = value->minute;
    value->minute = ((temp / 10) << 4) | (temp % 10); /* 计算分 */

    temp = value->hour;
    value->hour = ((temp / 10) << 4) | (temp % 10); /* 计算时 */

    temp = value->day;
    value->day = ((temp / 10) << 4) | (temp % 10); /* 计算天 */

    temp = value->weekday;
    value->weekday = (temp % 10); /* 计算星期 */

    temp = value->month;
    value->month = ((temp / 10) << 4) | (temp % 10); /* 计算月 */

    temp = value->year;
    if (temp > 100)
    {
        temp -= 100;
        value->month |= (1 << 7);
    }

    value->year = ((temp / 10) << 4) | (temp % 10); /* 计算年 */

    pcf8563_write(&pcf8563_drv.parent, 0x02, (void *)value, sizeof(struct pcf8563_time) / sizeof(unsigned char)); /* 将值写入寄存器 */
}
